//
// Created by mabinjie on 2024/10/28.
//

#ifndef JTT808_PACKET_HPP
#define JTT808_PACKET_HPP

#define BEGIN_OF_HEADER 1
#define END_OF_HEADER 21
#define BEGIN_OF_MESSAGE 22
#define END_OF_MESSAGE(all_size) (all_size - 3)
#define LENGTH_OF_HEADER (END_OF_HEADER - BEGIN_OF_HEADER + 1)
#define LENGTH_OF_MESSAGE(all_size) (END_OF_MESSAGE(all_size) - BEGIN_OF_HEADER + 1)

#include <cstdint>

#include "jtt808_header.hpp"
#include "common/serializer.hpp"
#include "protocol/tools/message_factory.hpp"
#include "protocol/tools/validate.hpp"


namespace protocol {

class jtt808_packet final : public common::Sizeable, public common::ToString {
private:
    writer_t writer_;
public:
    uint8_t begin_flag_ = 0x7e;
    jtt808_header header_;
    std::shared_ptr<jtt808_body> body_;
    uint8_t checksum_;
    uint8_t end_flag_ = 0x7e;

    IMPLEMENT_SIZEABLE(begin_flag_, header_, *body_, checksum_, end_flag_)
    IMPLEMENT_TOSTRING(begin_flag_, header_, *body_, checksum_, end_flag_)

    jtt808_packet() = delete;
    jtt808_packet(const jtt808_header &header, const std::shared_ptr<jtt808_body> body)
        : writer_(0), header_(header), body_(body) {}
    jtt808_packet(const jtt808_header &header, const writer_t &writer) : writer_(writer), header_(header) {

    }
    explicit jtt808_packet(reader_t &reader) : writer_(0) {
        jtt808_packet::deserialize(reader);
    }

    ~jtt808_packet() override = default;

    std::shared_ptr<common::Serialized> serialize() {
        writer_t writer(2048, escaper);
        writer.writeByte(begin_flag_, false);
        header_.serialize(writer);
        if (writer_.size() != 0) {
            writer.combine(writer_);
        } else {
            body_->serialize(writer);
        }
        writer.writeChecksumAndEndFlag<uint8_t>(get_checksum, end_flag_);
        return writer.flushAndGet();
    }

    void deserialize(reader_t &reader) {
        if (reader.validate(&checksum_is_valid)) {
            uint8_t temp;
            reader.readByte(temp);
            if (temp != begin_flag_) {
                logger->error("Wrong begin flag which is 0x{}", common::toHex(&temp));
                throw common::Error(common::error::WrongFlag, "Wrong begin flag");
            }
            header_.deserialize(reader);
            body_ = MessageFactory::create(header_);
            body_->deserialize(reader);
            reader.readByte(checksum_);
            reader.readByte(temp);
            if (temp != end_flag_) {
                logger->error("Wrong end flag which is 0x{}", common::toHex(&temp));
                throw common::Error(common::error::WrongFlag, "Wrong end flag");
            }
        } else {
            logger->error("checksum is invalid!");
            throw common::Error(common::error::WrongFlag, "checksum is invalid");
        }
    }

private:
    logger_t logger = common::Logger::logger("jtt808 packet");
};

typedef std::shared_ptr<jtt808_packet> Packet;

}

#endif //JTT808_PACKET_HPP
